package com.jzo2o.market.task;

import com.jzo2o.api.customer.CommonUserApi;
import com.jzo2o.api.customer.dto.response.CommonUserResDTO;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.market.constants.RedisConstants;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.redis.model.SyncMessage;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/*
 *   Description SyncMessageHandleTask
 *   @author zhangyan
 *   @time   2024/12/22
 */
@Component
@Slf4j
public class SyncMessageHandleTask {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ApplicationContext applicationContext;

    //批量处理同步消息
    @Async("taskExecutor")
    public void batchHandleSyncMessage(Long index) {
        log.info("线程：{}，索引号：{}", Thread.currentThread().getName(), index);
        String redisKey = String.format(RedisConstants.RedisKey.COUPON_SEIZE_SYNC_QUEUE_NAME, index);

        String lockName = "lock" + redisKey;
        RLock lock = redissonClient.getLock(lockName);

        List<SyncMessage<Object>> syncMessageList = null;
        SyncMessageHandleTask owner = applicationContext.getBean(SyncMessageHandleTask.class);
        try {
            boolean b = lock.tryLock(3, -1, TimeUnit.SECONDS);
            //加锁失败
            if (!b) {
                log.error("线程：{}，索引号：{},获取锁失败", Thread.currentThread().getName(), index);
                return;
            }

            //3. 加锁成功，批量处理数据
            log.info("线程：{}，索引号：{}，加锁成功", Thread.currentThread().getName(), index);
            syncMessageList = batchGetSyncMessage(redisKey);

        } catch (InterruptedException e) {
            e.printStackTrace();
            log.error("加锁异常", e);
        } finally {
            if (ObjectUtils.isNotEmpty(syncMessageList)) {
                for (SyncMessage<Object> syncMessage : syncMessageList) {

                    Long userId = Long.valueOf(syncMessage.getKey());
                    Long ActivityId = Long.valueOf(syncMessage.getValue().toString());
                    log.info("线程：{}，索引号：{},开始执行同步操作", Thread.currentThread().getName(), index);
                    // 将数据同步到MYSQL表中 并 扣减库存(二合一事务方法)
                    owner.syncConponAndDecrStock(userId, ActivityId);
                    //删除redis同步数据
                    Long delete = redisTemplate.opsForHash().delete(redisKey, syncMessage.getKey());
                    if (delete > 0) {
                        log.info("线程：{}，索引号：{},删除redis同步数据成功", Thread.currentThread().getName(), index);
                    }
                }
            }
            //释放锁
            log.info("线程：{}，索引号：{},释放锁", Thread.currentThread().getName(), index);
            lock.unlock();
        }


    }

    @Autowired
    private CommonUserApi commonUserApi;
    @Autowired
    private IActivityService activityService;
    @Autowired
    private ICouponService couponService;

    /**
     * 同步优惠券库存
     *
     * @param userId
     * @param activityId
     */
    @Transactional(rollbackFor = Exception.class)
    public void syncConponAndDecrStock(Long userId, Long activityId) {
        CommonUserResDTO user = commonUserApi.findById(userId);
        if (ObjectUtils.isEmpty(user)){
            throw new CommonException("用户不存在");
        }

        Activity activity = activityService.getById(activityId);
        if (ObjectUtils.isEmpty(activity)){
            throw new CommonException("活动不存在");
        }

        //构造优惠券
        Coupon coupon = BeanUtils.copyBean(activity, Coupon.class);
        coupon.setUserId(userId);
        coupon.setUserName(user.getNickname());
        coupon.setUserPhone(user.getPhone());
        coupon.setActivityId(activityId);
        coupon.setStatus(CouponStatusEnum.NO_USE.getStatus());
        coupon.setValidityTime(DateUtils.now().plusDays(activity.getValidityDays()));

        boolean save = couponService.save(coupon);
        if (!save){
            throw new CommonException("优惠券领取失败");
        }

        //同时删除库存
        Boolean decredStock= activityService.decrStock(activityId);
        if (!decredStock){
            throw new CommonException("库存扣减失败");
        }

    }



    /**
     * 批量获取同步消息
     *
     * @param redisKey
     */
    private List<SyncMessage<Object>> batchGetSyncMessage(String redisKey) {
        //1. 通过scan方式获取hash的游标对象（包括批量取出的全部数据）
        Cursor<Map.Entry<String, Object>> cursor = redisTemplate.opsForHash().scan(redisKey, ScanOptions.scanOptions().count(10).build());

        if (cursor == null) {
            return null;
        }
        //2. 解析游标对象中所有的数据，构建同步数据列表
        List<SyncMessage<Object>> list = cursor.stream().map(x -> SyncMessage.builder().key(x.getKey()).value(x.getValue()).build()).collect(Collectors.toList());
        //3. 关闭游标
        cursor.close();
        return list;
    }

}
